////////////////////////////////////////////////////////////////////////////////
//
//	This file is part of Woozox, see <http://code.google.com/p/woozox/>.
//
//	Copyright 2008-2009 Florian David
//
//	Woozoo is free software: you can redistribute it and/or modify
//	it under the terms of the GNU Lesser General Public License as 
//	published by the Free Software Foundation, either version 3 of 
//	the License, or (at your option) any later version.
//
//	Woozoo is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU Lesser General Public 
//	License along with Woozoo. If not, see <http://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////////////
package org.woozoo.xhtml.core.base.selector
{
	import org.woozoo.xhtml.core.base.Element;
	import org.woozoo.xhtml.core.util.ElementArrayUtil;
	
	/**
	 * @private
	 * */
	internal class AbstractSelectorOperatorNthChild extends AbstractSelectorOperator
	{
		protected var _type : String;
		private var __firstOrLast : Boolean = false;
		private var __multiplicater : int;
		private var __adder : int;
		private var __last : Boolean;
		private var __ofType : Boolean;
		
		override protected function _prepareString(str:String):String {
			return str;
		}
		
		override protected function _operateElements() : Array {
			if (!_type) {
				throw new Error('AbstractSelectorOperatorNthChild is an abstract class');
			}
			
			debugFunction('we interpret '+_type+' '+ str);
			__firstOrLast = _type.indexOf('nth') == -1;
			__ofType = _type.indexOf('type') >= 0;
			__last = _type.indexOf('last') >= 0;
			
			if (!__firstOrLast) {
				var o : Object = __getAdderAndMultiplicaterFromGrammar(str, _type);
				__adder = o.adder;
				__multiplicater = o.multicater;
			} else {
				__adder = 1;
				__multiplicater = 0;
			}
			if (__ofType) {
				return __operateElementsWithChildOfType();
			} else {
				return __operateElementsWithChild();
			}
		}
		
		private function __operateElementsWithChildOfType() : Array {
			var res : Array = [];
			var found : Boolean = false
			for (var i : int = 0; i < elementsLength; i++) {
				var index : int = __getIndex(i, elementsLength);
				if (index >= 0 && index < elementsLength) {
					res.push(getElementAt(index));
					if (__multiplicater == 0) {
						break;
					}
				}
			}
			return res;
		}
		
		private function __operateElementsWithChild() : Array {
			var res : Array = [];
			var indexes : Array;
			var groupByParent : Object = ElementArrayUtil.groupByParent(elements);
			
			for each (var list : Array in groupByParent) {
				var numChildren : int = (list[0] as Element).parent.numChildren;
				var len : int = list.length;
				for (var i : int = 0; i < len; i++) {
					var element : Element = list[i];
					if (i == 0) {
						indexes = __getIndexes(numChildren);
					}
					if (indexes.indexOf(element.siblingIndex) >= 0) {
						res.push(element);
					}
				}
			}
			return res;
		}
		
		private function __getIndexes(numChildren : int) : Array {
			var res : Array = [];
			var i : int = 0
			do {
				var index : int = __getIndex(i, numChildren);
				if (__last && index < 0) break;
				if (!__last && index >= numChildren) break;
				if (index >= 0 && index < numChildren) {
					res.push(index);
				}
				if (__multiplicater == 0) break;
				i++;
			} while (1);
			return res;
		}

		private function __getIndex(i : int, len : int) : int {
			var index : int;
			if (__last) {
				index = int(len - 1 - ( __multiplicater * i + __adder - 1));
			} else {
				index = int(__multiplicater * i + __adder - 1);
			}
			return index;
		}
		
		private function __getAdderAndMultiplicaterFromGrammar(input : String, replace : String) : Object {
			var g : String = __getGrammarFromString(input, replace);
			return __getObjectFromGammar(g);
		}
		
		private function __getGrammarFromString(input : String, replace : String) : String {
			var grammar : String = input.replace(':'+replace+'(', '').replace(')','');
			// remove all spaces
			grammar = grammar.replace(/[ ]*/, '');
			if (grammar == "odd") grammar = "2n+1";
			if (grammar == "even") grammar = "2n+0";
			if (grammar == "n") grammar = "1n";
			return grammar;
		}
		
		private function __getObjectFromGammar(grammar : String) : Object {
			var o : Object = {};
			var split : Array = grammar.split('n');
			if (split.length == 2) {
				o.multicater = int(split[0]);
				o.adder = int(split[1]);
			} else {
				o.multicater = 0;
				o.adder = int(split[0]);
			}
			return o;
		}
	}
}